package org.bimserver.shared.meta; /****************************************************************************** * Copyright (C) 2009-2014 BIMserver.org * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *****************************************************************************/ import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; import javax.jws.WebService; import org.bimserver.shared.interfaces.PublicInterface; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.Javadoc; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.TagElement; import org.eclipse.jdt.core.dom.TextElement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SService { private static final Logger LOGGER = LoggerFactory.getLogger(SService.class); private final Map<String, SMethod> methods = new TreeMap<String, SMethod>(); private final String fullName; private final Class<? extends PublicInterface> interfaceClass; private boolean processJavaDoc = true; private SServicesMap servicesMap; private String simpleName; private SourceCodeFetcher sourceCodeFetcher; private String nameSpace; public SService(SServicesMap servicesMap, SourceCodeFetcher sourceCodeFetcher, Class<? extends PublicInterface> interfaceClass) { this.servicesMap = servicesMap; this.sourceCodeFetcher = sourceCodeFetcher; this.interfaceClass = interfaceClass; this.nameSpace = interfaceClass.getAnnotation(WebService.class).targetNamespace(); this.fullName = interfaceClass.getAnnotation(WebService.class).targetNamespace() + "." + interfaceClass.getAnnotation(WebService.class).name(); this.simpleName = interfaceClass.getAnnotation(WebService.class).name(); } private void processClass(Class<?> clazz) { if (clazz == Class.class) { return; } for (Class<?> x : clazz.getInterfaces()) { processClass(x); } // Parents first, subclasses their documentation have precedence extractJavaDoc(clazz); } private void extractJavaDoc(Class<?> clazz) { ASTParser parser = ASTParser.newParser(AST.JLS4); parser.setSource(sourceCodeFetcher.get(clazz).toCharArray()); parser.setKind(ASTParser.K_COMPILATION_UNIT); final CompilationUnit cu = (CompilationUnit) parser.createAST(null); cu.accept(new ASTVisitor() { MethodDeclaration currentMethod = null; public boolean visit(Javadoc javaDoc) { if (currentMethod != null) { SMethod method = getSMethod(currentMethod.getName().getIdentifier()); if (method == null) { LOGGER.error("Method " + currentMethod.getName().getIdentifier() + " not found in class"); } else { for (Object tag : javaDoc.tags()) { if (tag instanceof TagElement) { TagElement tagElement = (TagElement) tag; String tagName = tagElement.getTagName() == null ? null : tagElement.getTagName().trim(); if ("@param".equals(tagName)) { SParameter parameter = null; for (int i = 0; i < tagElement.fragments().size(); i++) { Object fragment = tagElement.fragments().get(i); if (i == 0 && fragment instanceof SimpleName) { parameter = method.getParameter(((SimpleName) fragment).getIdentifier()); } else if (i == 1 && parameter != null && fragment instanceof TextElement) { parameter.setDoc(((TextElement) fragment).getText()); } } } else if ("@return".equals(tagName)) { method.setReturnDoc(extractFullText(tagElement)); } else if ("@throws".equals(tagName)) { } else { method.setDoc(extractFullText(tagElement)); } } } } } return super.visit(javaDoc); } @Override public boolean visit(MethodDeclaration node) { currentMethod = node; return super.visit(node); } @Override public void endVisit(MethodDeclaration node) { currentMethod = null; super.endVisit(node); } }); } private String extractFullText(TagElement tagElement) { StringBuilder builder = new StringBuilder(); for (Object fragment : tagElement.fragments()) { if (fragment instanceof TextElement) { TextElement textElement = (TextElement) fragment; builder.append(textElement.getText() + " "); } } return builder.toString().trim(); } public void init() { for (Method method : interfaceClass.getMethods()) { getServicesMap().addType(method.getReturnType()); if (getGenericType(method) != null) { getServicesMap().addType(getGenericType(method)); } for (Class<?> paramType : method.getParameterTypes()) { getServicesMap().addType(paramType); } } for (SClass sType : getServicesMap().getTypes()) { sType.init(); } for (Method method : interfaceClass.getMethods()) { methods.put(method.getName(), new SMethod(this, method)); } if (processJavaDoc && sourceCodeFetcher != null) { processClass(interfaceClass); } } private Class<?> getGenericType(Method method) { Type genericReturnType = method.getGenericReturnType(); if (method.getGenericReturnType() instanceof ParameterizedType) { ParameterizedType parameterizedTypeImpl = (ParameterizedType) genericReturnType; if (parameterizedTypeImpl.getActualTypeArguments()[0] instanceof Class) { return (Class<?>) parameterizedTypeImpl.getActualTypeArguments()[0]; } else if (parameterizedTypeImpl.getActualTypeArguments()[0] instanceof GenericArrayType) { // GenericArrayType genericArrayType = (GenericArrayType)parameterizedTypeImpl.getActualTypeArguments()[0]; // Type genericComponentType = genericArrayType.getGenericComponentType(); // if (genericComponentType instanceof Class) { // return (Class<?>)genericComponentType; // } } } else if (method.getGenericReturnType() instanceof TypeVariable) { } else if (method.getGenericReturnType() instanceof GenericArrayType) { } else { return (Class<?>) method.getGenericReturnType(); } return null; } public String getName() { return fullName; } public Set<SMethod> getMethods() { return new LinkedHashSet<SMethod>(methods.values()); } public SMethod getSMethod(String name) { return methods.get(name); } public Class<? extends PublicInterface> getInterfaceClass() { return interfaceClass; } // public void dump() { // System.out.println(getMethods().size()); // for (SMethod method : getMethods()) { // System.out.println(method.getName() + ": " + method.getReturnType().getName() + " (" + method.getDoc() + ")"); // for (SParameter parameter : method.getParameters()) { // System.out.println("\t" + parameter.getName() + " " + parameter.getType().getName() + " (" + parameter.getDoc() + ")"); // } // System.out.println(); // } // for (SClass type : getTypes()) { // System.out.println(type.getName()); // for (SField sField : type.getAllFields()) { // SClass type2 = sField.getType(); // if (type2 == null) { // System.err.println("type for " + sField.getName() + " = null"); // } else { // System.out.println("\t" + sField.getName() + " " + type2.getName()); // } // } // System.out.println(); // } // } public SServicesMap getServicesMap() { return servicesMap; } public String getSimpleName() { return simpleName; } public SMethod getMethod(String methodName) { return methods.get(methodName); } public String getNameSpace() { return nameSpace; } }